package org.hzero.interfaces.common.utils;

import org.hzero.interfaces.common.constants.KnifeInterfaceCodeEnum;
import org.hzero.interfaces.common.handler.KnifeCglibHandler;
import org.hzero.interfaces.common.handler.KnifeJdkHandler;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Proxy;

/**
 * 创建RestTemplate的代理
 *
 * @author: tianhao.luo@hand-china.com 2021/7/1  下午12:29
 */
public class KnifeRestTemplateProxyUtil {

    /**
     * 创建cglib代理对象
     * {@link KnifeCglibHandler}自定义的Cglib处理器
     *
     * @param restTemplate 被代理的restTemplate
     * @param knifeInterfaceCodeEnum 接口编码及描述枚举
     * @param methodNames  需要进行监控的方法名
     * @return 代理对象
     */
    public static RestTemplate createCglibProxy(RestTemplate restTemplate, KnifeInterfaceCodeEnum knifeInterfaceCodeEnum, String... methodNames) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RestTemplate.class);
        KnifeCglibHandler pmsRestTemplateCglibHandler = new KnifeCglibHandler(restTemplate, knifeInterfaceCodeEnum,methodNames);
        enhancer.setCallback(pmsRestTemplateCglibHandler);
        return (RestTemplate) enhancer.create();
    }

    /**
     * 创建jdk代理对象
     * {@link KnifeJdkHandler}自定义的jdk动态代理处理器
     *
     * @param restTemplate 被代理的restTemplate
     * @param knifeInterfaceCodeEnum 接口编码及描述枚举
     * @param methodNames  需要进行监控的方法名
     * @return 代理对象
     */
    public static RestOperations createJdkProxy(RestTemplate restTemplate, KnifeInterfaceCodeEnum knifeInterfaceCodeEnum, String... methodNames) {
        // 类加载机制：
        // 1 加载：读取Class文件到运行时区域的方法区
        // 2 连接（验证->准备->解析）：验证确保Class文件符合当前虚拟机要求，准备将为类变量分配内存并设置初始值（如果是final修饰则直接赋值），解析将常量池中的符号引用替换为直接引用
        // 3 初始化：执行类中的静态语句块和变量的赋值操作
        // 4 使用
        // 5 卸载
        // 类加载器：
        // 1 启动类加载器：加载jdk下lib目录中的类库
        // 2 扩展类加载器：加载jdk下lib/ext目录中的类库
        // 3 应用程序类加载器：负责加载用户路径classpath上的类库
        // 4 自定义加载器
        return (RestOperations) Proxy.newProxyInstance(KnifeJdkHandler.class.getClassLoader(),
                new Class[]{RestOperations.class},
                new KnifeJdkHandler(restTemplate, knifeInterfaceCodeEnum,methodNames));
    }
}
